home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / utility / csap421.zip / CSAP.C next >
C/C++ Source or Header  |  1994-02-25  |  20KB  |  603 lines

  1. /*
  2.    ****************************  NOTICE!  **************************
  3.    *   Contrary to the current trend  in  MS-DOS  software  this   *
  4.    *   program,  for  whatever  it is worth,  is NOT copyrighted   *
  5.    *   (with the exception of the runtime library  from  Borland   *
  6.    *   International's  Turbo  C)!  The program,  in whole or in   *
  7.    *   part,  may be used freely in any fashion  or  environment   *
  8.    *   desired.  If  you  find this program to be useful to you,   *
  9.    *   do NOT send any contribution to the author;  in the words   *
  10.    *   of  Rick  Conn,   'Enjoy!'  However,   if  you  make  any   *
  11.    *   improvements,  I would enjoy  receiving  a  copy  of  the   *
  12.    *   modified  source.  I can be reached at the following:       *
  13.    *                                                               *
  14.    *                on CompuServ:           70410,1004             *
  15.    *                on Channel 1 BBS        (xxx) xxx-xxx          *
  16.    *   or by mail or phone:                                        *
  17.    *                                                               *
  18.    *                Don A. Williams                                *
  19.    *                3913 W. Solano Dr. N.                          *
  20.    *                Phoenix, AZ  85019                             *
  21.    *                (602) 841-5333                                 *
  22.    *                                                               *
  23.    *   Every  effort has been made to avoid error and moderately   *
  24.    *   extensive testing has been  performed  on  this  program,   *
  25.    *   however, the author does not warrant it to be fit for any   *
  26.    *   purpose  or  to  be  free  from  error  and disclaims any   *
  27.    *   liability for actual or any other damage arising from the   *
  28.    *   use of this program.                                        *
  29.    *                                                               *
  30.    *   Calls to absread/abswrite modified for DOS 4+ and large     *
  31.    *   disk partition sizes                                        *
  32.    *                                                               *
  33.    *              Edgar W. Swank                                   *
  34.    *              5515 Spinnaker Dr., #4                           *
  35.    *              San Jose, CA 95123                               *
  36.    *              (408)227-3471                                    *
  37.    *              Internet: edgar@spectrx.saigon.com               *
  38.    *****************************************************************
  39. */
  40.  
  41.  
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <dos.h>
  46. #include <ctype.h>
  47. #include <dir.h>
  48. #include <mem.h>
  49. #include <alloc.h>
  50.  
  51. #define MAIN
  52.  
  53. #include "dosstruc.h"
  54.  
  55. /*---   Function Prototypes  ---*/
  56.  
  57. void            Usage(void);
  58. void            GetDPB(int Disk, struct DpbStruct * Dpb);
  59. unsigned        GetDosVersion(void);
  60. char           *strrspn(char *s1, char *s2);
  61. int             AbortProgram(void);
  62. void            SortDir(void);
  63. void            INT3(void);
  64.  
  65. /*---   End of Prototypes  ---*/
  66.  
  67. #define MAX12BIT 0x0FF6
  68. #define MAX16BIT 0xFFF6
  69.  
  70. #if defined(__TINY__)
  71.     #define MODEL "Tiny"
  72. #elif defined(__SMALL__)
  73.     #define MODEL "Small"
  74. #elif defined(__COMPACT__)
  75.     #define MODEL "Compact"
  76. #elif defined(__MEDIUM__)
  77.     #define MODEL "Medium"
  78. #elif defined(__LARGE__)
  79.     #define MODEL "Large"
  80. #elif defined(__HUGE__)
  81.     #define MODEL "Huge"
  82. #endif
  83.  
  84. char            Disk;        /* Alpha working disk ('A', 'B', .... )          */
  85. char            CurDir[67];    /* Storage for Current Directory of disk         */
  86. char            Path[67];    /* Storage for Path to sort                                      */
  87. char            Parent[67];    /* Storage for Parent part of Path                       */
  88. char            Element[13];    /* Storage for Child part of Path                        */
  89. char            Line[80];    /* Working storage for strings                           */
  90. char            Order = 'N';    /* Sort key indicator (Default=Name/Ext)         */
  91. char            Inverse = 0;    /* Ascend/Descend indic. (Default=Ascend)        */
  92. char            Level = 0;    /* Recursive sort indic. (default=Recursive) */
  93. char            RSwt = 0;    /* Report switch (Default=No Report)             */
  94. char            VerSwt = 0;    /* Pause for operator verify (Default=off)       */
  95. char            Packed = 1;    /* Elim. "erased" entries (Default=on)           */
  96. char            TruncateSwt = 0;/* Truncate directories (Default=off)    */
  97. char            FatDirty = 0;    /* FAT needs to be rewritten                             */
  98. int             Is12Bit;    /* 12 / 16 bit cluster indicator                         */
  99. int            *CluArray;    /* Cluster Array ptr, dynamically allocated      */
  100. int             Lim, i, j, k, l;
  101. long            m;
  102. int             OutSectors, OutClusters, BytesPerCluster, ECount;
  103. unsigned        LastCluster;    /* Value for end of cluster chain                        */
  104. unsigned        Cluster, Sector, NumSec;
  105. unsigned        FatSize;
  106. long            FatRecSize;
  107. short           FatRecCount;
  108.  
  109. struct FatStruct {
  110.     unsigned char  *Ptr;
  111.     unsigned        Size;
  112.     }              *FatArray;
  113.  
  114. struct absr32m  a32;
  115.  
  116. unsigned        Version;
  117. long            MinMem;        /* Minimum available memory                                      */
  118. unsigned        Freed = 0;    /* Freed cluster count                                           */
  119. unsigned        Version;
  120. unsigned        DirStart;
  121.  
  122. struct DpbStruct Dpb;        /* Disk Parameter Block (see dosstruc.h)         */
  123. struct ClusterQueue CluQ;    /* Queue of cluster for directory                        */
  124. struct DirEntry *DirBuff;    /* Buffer for directory to be sorted             */
  125. struct ExtendedEntry Dir;
  126. struct ClusterEntry *p, *t;
  127. struct ExtFcb   Fcb;
  128.  
  129.  void
  130.  main (int argc, char *argv[]) {
  131.     char           *strrspn();
  132.     void            SortDir(), Usage();
  133.     char           *p, *p1, t1;
  134.     int             i, j;
  135.  
  136.     bdos(0x0D, 0, 0);    /* Reset Disk Subsystem - Flush all buffers */
  137.     fputs("C-Sort And Pack [CSAP]: Version 4.2.1: Date: February 25, 1994", stderr);
  138.     fputs(" [", stderr);
  139.     fputs(MODEL, stderr);
  140.     fputs(" Model]\n", stderr);
  141.     fputs("    use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr);
  142.  
  143.     Disk = getdisk() + 'A';
  144.     Line[0] = '\\';
  145.     getcurdir(Disk - '@', &Line[1]);
  146.  
  147.     ctrlbrk(AbortProgram);    /* Install "wrap-up" in Control Break vec. */
  148.  
  149.     /* Interpret command line arguments, if any      */
  150.  
  151.     for (i = 1; i < argc; ++i) {
  152.         if (argv[i][0] == '-') {
  153.             for (j = 1; j < strlen(argv[i]); ++j) {
  154.                 switch (toupper(argv[i][j])) {
  155.                 case 'N':    /* Sort Key = Name/Ext (default)         */
  156.                     Order = 'N';
  157.                     break;
  158.                 case 'D':    /* Sort Key = Date/Time                          */
  159.                     Order = 'D';
  160.                     break;
  161.                 case 'E':    /* Sort Key = Ext/Name                           */
  162.                     Order = 'E';
  163.                     break;
  164.                 case 'S':    /* Sort Key = File Size                          */
  165.                     Order = 'S';
  166.                     break;
  167.                 case 'R':    /* Report Dir loc. & "erased"            */
  168.                     RSwt = 1;
  169.                     break;
  170.                 case 'I':    /* Sort order inverse                            */
  171.                     Inverse = 1;
  172.                     break;
  173.                 case 'P':    /* Do NOT remove "erased" entries                 */
  174.                     Packed = 0;
  175.                     break;
  176.                 case 'L':    /* Limit sort to one level                       */
  177.                     Level = 1;
  178.                     break;
  179.                 case 'V':    /* Request approval before sort          */
  180.                     VerSwt = 1;
  181.                     break;
  182.                 case 'T':    /* Truncate directories                          */
  183.                     TruncateSwt = 1;
  184.                     break;
  185.                 case 'H':
  186.                     Usage();
  187.                 default:    /* Illegal option                                        */
  188.                     fprintf(stderr, "Invalid option %s.\n", argv[i]);
  189.                     Usage();
  190.                     break;
  191.                 }
  192.             }
  193.         }
  194.         else {    /* Not switch, assume directory name or '?'              */
  195.             if (argv[i][0] == '?') Usage();
  196.             if (argv[i][1] == ':') {    /* Check for disk specified      */
  197.                 Disk = toupper(argv[i][0]);
  198.                 p = &argv[i][2];
  199.             }
  200.             else p = &argv[i][0];
  201.             if ((p[0] != '\\') && (p[0] != '/')) {
  202.                 Line[0] = '\\';
  203.                 getcurdir(Disk - '@', &Line[1]);
  204.                 p1 = &p[strcspn(p, "\\/")];
  205.                 t1 = *p1;
  206.                 *p1 = '\0';
  207.                 if (!strcmp(p, ".")) {
  208.                     p = p1;
  209.                     *p1 = t1;
  210.                 }
  211.                 else if (!strcmp(p, "..")) {
  212.                     while (!strcmp(p, "..")) {
  213.                         p = strrspn(Line, "\\/");
  214.                         if ((p - Line) == 0) ++p;
  215.                         *p = '\0';
  216.                         if (t1 != '\0') p = ++p1;
  217.                         else p = p1;
  218.                         p1 = &p[strcspn(p, "\\/")];
  219.                         t1 = *p1;
  220.                         *p1 = '\0';
  221.                     }
  222.                     *p1 = t1;
  223.                 }
  224.                 else *p1 = t1;
  225.                 if (*p != '\0') strcat(Line, "\\");
  226.                 strcat(Line, p);
  227.             }
  228.             else strcpy(Line, p);
  229.         }
  230.     }
  231.  
  232.     /*
  233.      * Get disk information - uses un-documented DOS call, Int 21H, Func.
  234.      * 32H This function has been verified to work correctly in PC/MS-DOS
  235.      * versions 2.0 through 3.3.  It is heavily used by DOS programs such
  236.      * as CHKDSK.
  237.      */
  238.  
  239.     GetDPB(Disk, &Dpb);
  240.     Version = GetDosVersion() & 0xFF;
  241.     switch (Version) {
  242.         case 2:
  243.             FatSize = Dpb.V.V2.FatSize;
  244.             DirStart = Dpb.V.V2.DirStart;
  245.             break;
  246.         case 3:
  247.             FatSize = Dpb.V.V3.FatSize;
  248.             DirStart = Dpb.V.V3.DirStart;
  249.             break;
  250.         case 4:
  251.         case 5:
  252.         case 6:
  253.             FatSize = Dpb.V.V4.FatSize;
  254.             DirStart = Dpb.V.V4.DirStart;
  255.             break;
  256.         default:
  257.             fprintf(stderr, "Invalid DOS version: %d\n", Version);
  258.             exit(1);
  259.     }
  260.     FatRecSize = (long) FatSize * Dpb.SectorSize;
  261.  
  262.     /* Establish whether disk has 16-bit or 12-bit clusters  */
  263.  
  264.     if (Dpb.LastCluster > MAX16BIT) {
  265.         fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n");
  266.         exit(1);
  267.     }
  268.     Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1;
  269.     LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8;
  270.  
  271.     /*
  272.      * Get & save current directory of working disk.  We have to change
  273.      * to sort and must restore on termination
  274.      */
  275.  
  276.     CurDir[0] = Disk;
  277.     CurDir[1] = ':';
  278.     CurDir[2] = '\\';
  279.     getcurdir(Disk - '@', (char *) &CurDir[3]);
  280.  
  281.     /* Allocate space to hold entire FAT in memory and read it in  */
  282.  
  283.     FatRecCount = ((FatRecSize + (long) (FAT_BLK - 1)) / FAT_BLK);    /* EWS */
  284.     if ((FatArray = malloc(FatRecCount * sizeof(struct FatStruct))) == NULL) {
  285.         fprintf(stderr, "Insufficient memory for FAT array.\n");
  286.         exit(1);
  287.     }
  288.     for (m = FatRecSize, i = 0; i < FatRecCount; i++, m -= (m > FAT_BLK) ? FAT_BLK : m) {
  289.         if ((FatArray[i].Ptr = malloc((m > FAT_BLK) ? FAT_BLK : (unsigned) m)) == NULL) {
  290.             fprintf(stderr, "Insufficient memory for FAT Array entry.\n");
  291.             exit(1);
  292.         }
  293.         FatArray[i].Size = (m > FAT_BLK) ? FAT_BLK : (unsigned) m;
  294.     }
  295.  
  296.     for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
  297.         if (absread(Disk - 'A', FatArray[i].Size / Dpb.SectorSize,
  298.                     (unsigned) m, FatArray[i].Ptr) != 0) {
  299. #if 0
  300.         a32.nsect = FatArray[i].Size / Dpb.SectorSize;
  301.         a32.sector = m;
  302.         a32.xferad = FatArray[i].Ptr;
  303.         if (absread(Disk - 'A', -1, 0, &a32) != 0) {    /* EWS */
  304. #endif
  305.             fprintf(stderr, "Error reading FAT.\n");
  306.             perror("");
  307.             exit(1);
  308.         }
  309.     }
  310.  
  311.     /*
  312.      * Develop full path name for directory to be sorted and separate
  313.      * into Parent and Child portions
  314.      */
  315.  
  316.     Path[0] = Parent[0] = Element[0] = '\0';
  317.     Path[0] = Disk;
  318.     Path[1] = ':';
  319.     Path[2] = '\0';
  320.     if ((Line[0] != '\\') && (Line[0] != '/')) {
  321.         strcat(Path, "\\");
  322.         strcpy(&Path[3], &CurDir[3]);
  323.         if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/'))
  324.             strcat(Path, "\\");
  325.     }
  326.     strcat(Path, Line);
  327.     p = strrspn(Path, "\\/");
  328.     strcpy(Element, &p[1]);
  329.     if (p[-1] == ':') p++;
  330.     strncpy(Parent, Path, (int) (p - Path));
  331.     Parent[(int) (p - Path)] = '\0';
  332.  
  333.     MinMem = coreleft();    /* Initialize minimum available memory           */
  334.  
  335.     /*
  336.      * Perform sort.  SortDir is recursive and, if Level is not on, will
  337.      * sort sort all levels of the hierarchy from the starting level down
  338.      */
  339.  
  340.     SortDir();
  341.  
  342.     printf("Minimum memory= %ld\n", MinMem);
  343.  
  344.     bdos(0x0D, 0, 0);    /* Reset disk subsystem - flush all buffers */
  345.     if (FatDirty) {
  346.         for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
  347.             if (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0) {
  348.                 fprintf(stderr, "Error writing FAT.\n");
  349.                 perror("");
  350.                 exit(1);
  351.             }
  352.         }
  353.         if (Dpb.FatCopies == 2) {
  354.             for (i = 0, m = Dpb.FatStart + FatSize; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
  355.                 if (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0) {
  356.                     fprintf(stderr, "Error writing 2nd copy of FAT.\n");
  357.                     perror("");
  358.                     exit(1);
  359.                 }
  360.             }
  361.         }
  362.         printf("There %s %d cluster%s (%d bytes) freed\n",
  363.                (Freed == 1) ? "was" : "were",
  364.                Freed,
  365.                (Freed == 1) ? "" : "s",
  366.                Freed * Dpb.SectorSize * (Dpb.ClusterSize + 1));
  367.     }
  368.     bdos(0x0D, 0, 0);    /* Reset disk subsystem - flush all buffers */
  369.     bdosptr(0x3B, CurDir, 0);    /* Restore input "current" directory     */
  370. }                /* end Main */
  371.  
  372.  
  373. /*
  374.  * STRRSPN is simply a reverse version of STRSPN.  It finds the LAST
  375.  * occurance in S1 of any member of S2.  For some reason, none of the C
  376.  * compilers that I use provide this although they all provide STRSPN
  377.  */
  378.  
  379.  char           *
  380. strrspn (char *s1, char *s2) {
  381.     char           *p1;
  382.  
  383.     p1 = s1 + strlen(s1) - 1;
  384.     while (p1 >= s1) {
  385.         if (strchr(s2, *p1) != NULL) return (p1);
  386.         --p1;
  387.     }
  388.     return ((char *) NULL);
  389. }
  390.  
  391.  
  392. /*
  393.  * SearchFirst --  Search for First Directory Entry. On entry fcb contains an
  394.  * extended File Control Block with file name and attribute bits set.  On
  395.  * exit, fcb contains matched entry unless return code is 255, in which case
  396.  * no match was found.  This routine is used instead of the ones provided by
  397.  * the compiler so that the cluster information for the directory can be
  398.  * obtained.
  399.  */
  400.  
  401.  int
  402. SearchFirst (struct ExtFcb * Fcb) {
  403.     union REGS      regs;
  404.  
  405.     regs.x.ax = 0x1100;
  406.     regs.x.dx = (unsigned) Fcb;
  407.     intdos(®s, ®s);
  408.     return ((int) (regs.x.ax & 0xFF));
  409. }
  410.  
  411.  
  412.  
  413. /*
  414.  * Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative
  415.  * sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute
  416.  * Disk Write [interrupt 26H].  Requires access to the undocumented DOS Disk
  417.  * Parameter Block [use funtion GetDPB].
  418.  */
  419.  
  420.  long
  421. Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) {
  422.  
  423.     return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart);
  424. }
  425.  
  426.  
  427.  
  428. /*
  429.  * NextCl -- This function calculates the logical "chaining" of cluster
  430.  * numbers in a File Allocation Table [FAT].  Given an entry cluster number
  431.  * it calculates the next cluster using the array Fat[].
  432.  *
  433.  * If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise
  434.  * Fat[] is assumed to contain 16 bit entries.
  435.  */
  436.  
  437.  unsigned
  438. NextCl (int Is12Bit, unsigned Cluster) {
  439.     unsigned        ClWord, ClOffset;
  440.     long            Factor = FAT_BLK / 2;
  441.  
  442.     if (Is12Bit) {        /* 12 bit FAT lookup */
  443.         ClOffset = 3 * Cluster / 2;
  444.         ClWord = (FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] & 0xFF)
  445.             + (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8);
  446.         if (Cluster & 1) return (ClWord >> 4);    /* odd cluster  */
  447.         else return (ClWord & 0x0FFF);    /* even cluster */
  448.     }
  449.     else
  450.         return (((unsigned int *) (FatArray[Cluster / Factor].Ptr))[Cluster % Factor]);
  451. }
  452.  
  453.  
  454.  void
  455. FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster) {
  456.     extern char     FatDirty;
  457.     extern unsigned Freed;
  458.     unsigned        ClWord, ClOffset;
  459.  
  460.     if (Is12Bit) {        /* 12 bit FAT lookup */
  461.         ClOffset = 3 * Cluster / 2;
  462.         ClWord = FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK]
  463.             + (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8);
  464.         if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0);    /* odd   */
  465.         else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF);
  466.         FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] = ClWord >> 8;
  467.         FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] = ClWord & 0xFF;
  468.     }
  469.     else
  470.         ((unsigned int *) FatArray[Cluster / INT_FAT_BLK].Ptr)[Cluster % INT_FAT_BLK] = Val;    /* 16 bit FAT lookup */
  471.     if (!Val) ++Freed;
  472. }
  473.  
  474.  
  475. /*
  476.  * PutQueue -- Builds a simple FIFO linked list using dynamically acquired
  477.  * memory.
  478.  */
  479.  
  480.  void
  481. PutQueue (struct ClusterQueue * Q, unsigned Cluster) {
  482.     struct ClusterEntry *p;
  483.  
  484.     if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) {
  485.         fprintf(stderr, "Insufficient memory(1).\n");
  486.         AbortProgram();
  487.     }
  488.     p->Next = NULL;
  489.     p->Cluster = Cluster;
  490.     if (Q->Head == NULL) Q->Head = p;
  491.     else Q->Current->Next = p;
  492.     Q->Current = p;
  493.     Q->Count++;
  494. }
  495.  
  496.  
  497. /*
  498.  * AbortProgram -- Aborts the program, resetting the current directory, with
  499.  * an error code of 1.
  500.  */
  501.  
  502.  int
  503. AbortProgram (void) {
  504.  
  505.     bdos(0x0D, 0, 0);    /* Reset disk subsystem - flush all buffers */
  506.     bdosptr(0x3B, CurDir, 0);    /* Reset input Current Directory */
  507.     exit(1);
  508.     return (0);
  509. }
  510.  
  511.  
  512. /*
  513.  * strincmp -- The comparsion routine for the qsort algorithm.
  514.  */
  515.  
  516.  int
  517. strincmp (struct DirEntry * a, struct DirEntry * b) {
  518.     long            t;
  519.  
  520.     /*
  521.      * Ensure that "erased" entries sort high no matter what the sort key
  522.      * is.
  523.      */
  524.  
  525.     if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1);
  526.     if (b->Name[0] == 0xE5) return (-1);
  527.  
  528.     /*
  529.      * Ensure that directories sort lower than files no matter what sort
  530.      * key
  531.      */
  532.  
  533.     if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) {
  534.         if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) {
  535.             if (a->Attribute & 0x10) return (-1);
  536.             else return (1);
  537.         }
  538.     }
  539.  
  540.     /* Actual sort key compare routines  */
  541.  
  542.     switch (Order) {
  543.         case 'D':        /* Sort key is Date/Time                         */
  544.             if (a->ModifyDate < b->ModifyDate) t = -1;
  545.             else if (a->ModifyDate > b->ModifyDate) t = 1;
  546.             else {
  547.                 if (a->ModifyTime < b->ModifyTime) t = -1;
  548.                 else if (a->ModifyTime > b->ModifyTime) t = 1;
  549.                 else t = 0;
  550.             }
  551.             break;
  552.         case 'N':        /* Sort key is Name/Ext (default)        */
  553.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  554.             break;
  555.         case 'E':        /* Sort key is Ext/Name                          */
  556.             if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) {
  557.                 t = strncmp((char *) a->Name, (char *) b->Name, 8);
  558.             }
  559.             break;
  560.         case 'S':        /* Sort key is File Size                         */
  561.             t = a->FileSize - b->FileSize;
  562.             break;
  563.         default:
  564.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  565.             break;
  566.     }
  567.     if (Inverse) t = -t;        /* Sort order is inverse         */
  568.     return ((t < 0) ? -1 : ((t > 0) ? 1 : 0));
  569. }
  570.  
  571.  unsigned
  572. GetDosVersion (void) {
  573.     union REGS      Regs;
  574.  
  575.     Regs.h.ah = 0x30;
  576.     intdos(&Regs, &Regs);
  577.     return (Regs.x.ax);
  578. }
  579.  
  580.  
  581.  void
  582. Usage (void) {
  583.  
  584.     printf("USAGE:    CSAP [options] [[d:]directory_name]\n");
  585.     printf("                        or\n");
  586.     printf("          CSAP [[d:]directory_name] [options]\n");
  587.     printf("\n");
  588.     printf("Options:\n");
  589.     printf("    -N    Sort on Name/Ext (default).\n");
  590.     printf("    -D    Sort on Date/Time.\n");
  591.     printf("    -E    Sort on Ext/Name.\n");
  592.     printf("    -S    Sort on File Size.\n");
  593.     printf("\n");
  594.     printf("    -R    Report number of \"erased\" entries and directory location.\n");
  595.     printf("    -I    Inverse sort order, i.e. descending.\n");
  596.     printf("    -P    Do NOT remove \"erased\" entries.\n");
  597.     printf("    -L    Limit sort to a single level.\n");
  598.     printf("    -V    Request confirmation before sorting.\n");
  599.     printf("    -T    Truncate directories.\n");
  600.     printf("    -H    This message.\n");
  601.     exit(0);
  602. }
  603.